/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: xresult.h
 *
 * Purpose: the result of function define
 *
 * Developer:
 *   wen.gu , 2023-07-11
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __XBUS_CORE_XRESULT_H__
#define __XBUS_CORE_XRESULT_H__

#include <string>
#include <memory>
#include <optional>
#include <variant>
#include "xbus/core/xerror.h"


namespace xbus::core {

template <typename T, typename E>
class XResult;

namespace internal {

/// @brief Trait that detects whether a type is a XResult<...>
template <typename U>
struct is_result : std::false_type {};

template <typename U, typename G>
struct is_result<XResult<U, G>> : std::true_type {};

} //namespace internal

/**
 * @brief This class is a type that contains either a value or an error
 * 
 * @tparam T the type of value
 * @tparam E the type of errora
 * @uptrace{SWS_CORE_00701}
 */
template <typename T, typename E = XErrC>
class XResult final {
public:
    template <typename U, typename... Args>
    struct has_as_first_checker;

    template <typename U>
    struct has_as_first_checker<U> : std::false_type
    {};

    template <typename U, typename... Args>
    struct has_as_first_checker<U, U, Args...> : std::true_type
    {};

    template <typename U, typename A, typename... Args>
    struct has_as_first_checker<U, A, Args...> : std::false_type
    {};

    template <typename U, typename... Args>
    struct has_as_first : public has_as_first_checker<
                        typename std::remove_reference<U>::type, typename std::remove_reference<Args>::type...>
    {};


public:    
    using value_type = T; //@uptrace{SWS_CORE_00711}
    using error_type = E; //@uptrace{SWS_CORE_00712}

public: //ctr/dtr
    /**
     * @brief Construct a new XResult from the specified value (given as lvalue).
     * 
     * @param[in] t the value to put into the XResult
     * @uptrace{SWS_CORE_00721}
     */
    XResult (const T &t) :data_(t) { } 

    /**
     * @brief Construct a new XResult from the specified value (given as rvalue).
     * 
     * @param[in] t the value to put into the XResult
     * @uptrace{SWS_CORE_00722}
     */
    XResult (T &&t):data_(std::move(t)) {}

    /**
     * @brief Construct a new XResult from the specified error (given as lvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @uptrace{SWS_CORE_00723}
     */
    explicit XResult (const E &e): data_(e) { }

    /**
     * @brief Construct a new XResult from the specified error (given as rvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @uptrace{SWS_CORE_00724}
     */
    explicit XResult (E &&e):data_(std::move(e)) { }

    /**
     * @brief Copy-construct a new XResult from another instance
     * 
     * @param[in] other the other instance
     * @uptrace{SWS_CORE_00725}
     */
    XResult (const XResult &other) = default;


    /**
     * @brief Move-construct a new XResult from another instance
     * 
     * @param[in] other the other instance
     * @uptrace{SWS_CORE_00726}
     */    
    XResult (XResult &&other) noexcept(std::is_nothrow_move_constructible<T>::value &&        
                                     std::is_nothrow_move_constructible< E >::value) = default;

    /**
     * @brief Destructor. 
     * This destructor is trivial if std::is_trivially_destructible<T>::value && 
     * std::is_trivially_destructible<E>::value is true.
     * 
     * @uptrace{SWS_CORE_00727}
     */
    ~XResult () noexcept = default;      

public: //Build a new XResult

    /**
     * @brief Build a new XResult from the specified value (given as lvalue).
     * 
     * @param[in] t the value to put into the XResult
     * @return XResult a XResult that contains the value t
     * @uptrace{SWS_CORE_00731}
     */
    static XResult FromValue (const T &t) { return XResult(t); }   

    /**
     * @brief Build a new XResult from the specified value (given as rvalue).
     * 
     * @param[in] t the value to put into the XResult
     * @return XResult a XResult that contains the value t
     * @uptrace{SWS_CORE_00732}
     */    
    static XResult FromValue (T &&t) { return XResult(std::move(t)); }

    /**
     * @brief Build a new XResult from a value that is constructed in-place from the given arguments
     * This function shall not participate in overload resolution unless: std::is_constructible<T,Args&&...>::value is 
     * true, and the first type of the expanded parameter pack is not T, and the first type of the expanded parameter 
     * pack is not a specialization of XResult
     * 
     * @tparam Args... the types of arguments given to this function 
     * @param[in] args the arguments used for constructing the value
     * @return XResult a XResult that contains a value
     * @uptrace{SWS_CORE_00733}
     */
    template <typename... Args, typename = typename std::enable_if<true && 
              std::is_constructible<T, Args&&...>::value &&  
              !has_as_first<T, Args...>::value && 
              !has_as_first<XResult, Args...>::value>::type
             >
    static XResult FromValue (Args &&... args) { return XResult(T{std::forward<Args>(args)...}); }


    /**
     * @brief Build a new XResult from the specified error (given as lvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @return XResult a XResult that contains the error e
     * @uptrace{SWS_CORE_00734}
     */
    static XResult FromError (const E &e) { return XResult(e); }

    /**
     * @brief Build a new XResult from the specified error (given as rvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @return XResult a XResult that contains the error e
     * @uptrace{SWS_CORE_00735}
     */
    static XResult FromError (E &&e) { return XResult(std::move(e)); }

    /**
     * @brief Build a new XResult from an error that is constructed in-place from the given arguments
     * This function shall not participate in overload resolution unless: std::is_constructible<E, Args&&...>::value is 
     * true, and the first type of the expanded parameter pack is not E, and the first type of the expanded parameter 
     * pack is not a specialization of XResult
     * 
     * @tparam Args...  the types of arguments given to this function
     * @param[in] args the arguments used for constructing the error
     * @return XResult a XResult that contains an error
     * @uptrace{SWS_CORE_00736}
     */
    template <typename... Args, 
              typename = typename std::enable_if<true && 
              std::is_constructible<E, Args&&...>::value && 
              !has_as_first<E, Args...>::value && 
              !has_as_first<XResult, Args...>::value >::type
             >
    static XResult FromError (Args &&... args) { return XResult(E{std::forward<Args>(args)...}); }

public: //assign
    /**
     * @brief Copy-assign another XResult to this instance
     * 
     * @param[in] other the other instance
     * @return XResult& *this, containing the contents of other
     * @uptrace{SWS_CORE_00741}
     */
    XResult& operator= (const XResult &other) = default;


    /**
     * @brief Move-assign another XResult to this instance
     * 
     * @param[in] other the other instance
     * @return XResult& *this, containing the contents of other
     * @uptrace{SWS_CORE_00742}
     */
    XResult& operator= (XResult &&other) noexcept(
        std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value &&std::is_nothrow_move_constructible< E >::value && std::is_nothrow_move_assignable< E >::value) = default;
    

    /**
     * @brief Put a new value into this instance, constructed in-place from the given arguments.
     * 
     * @tparam Args... the types of arguments given to this function
     * @param[in] args the arguments used for constructing the value
     * @uptrace{SWS_CORE_00743}
     */
    template <typename... Args>
    void emplaceValue (Args &&... args) {
        // Unfortunately, ara::core::std::variant does not support emplace(), so we fall back to assignment here.
        data_ = std::move(T(std::forward<Args>(args)...));
    }

    /**
     * @brief Put a new error into this instance, constructed in-place from the given arguments
     * 
     * @tparam Args... the types of arguments given to this function 
     * @param[in] args the arguments used for constructing the error
     * @uptrace{SWS_CORE_00744}
     */
    template <typename... Args>
    void emplaceError (Args &&... args) {
        // Unfortunately,ara::core::std::variant does not support emplace(), so we fall back to assignment here.
        data_ = std::move(E(std::forward<Args>(args)...));
    }

    /**
     * @brief Exchange the contents of this instance with those of other
     * 
     * @param[in,out] other the other instance
     * @uptrace{SWS_CORE_00745}
     */
    void swap (XResult &other) noexcept(
        std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value &&
        std::is_nothrow_move_constructible<E>::value &&std::is_nothrow_move_assignable<E>::value) {
        std::swap(data_, other.data_);
    }

public:
    /**
     * @brief Check whether *this contains a value.
     * 
     * @return true if *this contains a value
     * @return false otherwise
     * @uptrace{SWS_CORE_00751}
     */
    bool hasValue () const noexcept { return std::holds_alternative<T>(data_); }

    /**
     * @brief Check whether *this contains a value
     * 
     * @return true if *this contains a value
     * @return false otherwise
     * @uptrace{SWS_CORE_00752}
     */
    explicit operator bool () const noexcept { return hasValue(); }

    /**
     * @brief Access the contained value.
     * This function’s behavior is undefined if *this does not contain a value
     * 
     * @return const T& a const_reference to the contained value
     * @uptrace{SWS_CORE_00753}
     */
    const T& operator* () const & { return value(); }

    /**
     * @brief Access the contained value.
     * This function’s behavior is undefined if *this does not contain a value
     * 
     * @return T&&  an rvalue reference to the contained value
     * @uptrace{SWS_CORE_00759}
     */
    T&& operator* () && { return std::move(*this).value(); }

    /**
     * @brief Access the contained value.
     * This function’s behavior is undefined if *this does not contain a value
     * 
     * @return const T* a pointer to the contained value
     * @uptrace{SWS_CORE_00754} 
     */
    const T* operator-> () const { return std::addressof(value()); }

    /**
     * @brief Access the contained value.
     * This function’s behavior is undefined if *this does not contain a value
     * 
     * @return const T& a const reference to the contained value
     * @uptrace{SWS_CORE_00755} 
     */
    const T& value () const & { return std::get<T>(data_); }

    /**
     * @brief Access the contained value.
     * This function’s behavior is undefined if *this does not contain a value
     * 
     * @return T&& an rvalue reference to the contained value
     * @uptrace{SWS_CORE_00756} 
     */
    T&& value () && { return std::move(std::get<T>(data_)); }

    /**
     * @brief Access the contained error.
     * The behavior of this function is undefined if *this does not contain an error.
     * 
     * @return const E& a const reference to the contained error
     * @uptrace{SWS_CORE_00757}
     */
    const E& error () const & { return std::get<E>(data_); }

    /**
     * @brief Access the contained error.
     * The behavior of this function is undefined if *this does not contain an error.
     * 
     * @return E&& an rvalue reference to the contained error
     * @uptrace{SWS_CORE_00758}
     */
    E&& error () && { return std::move(std::get<E>(data_)); }

    /**
     * @brief Return the contained value as an std::optional.
     * 
     * @return std::optional<T> an std::optional with the value, if present
     * @uptrace{SWS_CORE_00770}
     */
    std::optional<T> ok () const & { return hasValue() ? std::optional<T>(value()) : std::nullopt; }

    /**
     * @brief Return the contained value as an std::optional.
     * 
     * @return std::optional<T> an std::optional with the value, if present
     * @uptrace{SWS_CORE_00771}
     */
    std::optional<T> ok () && { return hasValue() ? std::optional<T>(std::move(value())) : std::nullopt; }

    /**
     * @brief Return the contained error as an std::optional
     * 
     * @return std::optional<E> an std::optional with the error, if present
     * @uptrace{SWS_CORE_00772}
     */
    std::optional<E> err () const & { return hasValue() ? std::nullopt : std::optional<E>(error()); }

    /**
     * @brief Return the contained error as an std::optional
     * 
     * @return std::optional<E> an std::optional with the error, if present
     * @uptrace{SWS_CORE_00773}
     */
    std::optional<E> err () && { return hasValue() ? std::nullopt : std::optional<E>(std::move(error())); }

    /**
     * @brief Return the contained value or the given default value.
     * If *this contains a value, it is returned. Otherwise, the specified default value is returned, 
     * static_cast’d to T.
     * 
     * @tparam U the type of defaultValue
     * @param[in] defaultValue the value to use if *this does not contain a value
     * @return T the value
     * @uptrace{SWS_CORE_00761}
     */
    template <typename U>
    T valueOr (U &&defaultValue) const & {
        return hasValue() ? value() : static_cast<T>(std::forward<U>(defaultValue));
    }

    /**
     * @brief Return the contained value or the given default value.
     * If *this contains a value, it is returned. Otherwise, the specified default value is returned, 
     * static_cast’d to T.
     * 
     * @tparam U the type of defaultValue
     * @param[in] defaultValue the value to use if *this does not contain a value
     * @return T the value
     * @uptrace{SWS_CORE_00762}
     */  
    template <typename U>
    T valueOr (U &&defaultValue) && {
        return hasValue() ? std::move(value()) : static_cast<T>(std::forward<U>(defaultValue));
    }

    /**
     * @brief Return the contained error or the given default error.
     * If *this contains an error, it is returned. Otherwise, the specified default error is returned, 
     * static_cast’d to E.
     * 
     * @tparam G the type of defaultError
     * @param[in] defaultError the error to use if *this does not contain an error
     * @return E the error
     * @uptrace{SWS_CORE_00763}
     */
    template <typename G>
    E errorOr (G &&defaultError) const & {
        return hasValue() ? static_cast<E>(std::forward<G>(defaultError)) : error();
    }

    /**
     * @brief Return the contained error or the given default error.
     * If *this contains an error, it is returned. Otherwise, the specified default error is returned, 
     * static_cast’d to E.
     * 
     * @tparam G the type of defaultError
     * @param[in] defaultError the error to use if *this does not contain an error
     * @return E the error
     * @uptrace{SWS_CORE_00764}
     */
    template <typename G>
    E errorOr (G &&defaultError) && {
        return hasValue() ? static_cast<E>(std::forward<G>(defaultError)) : std::move(error());
    }

public:
    /**
     * @brief Return whether this instance contains the given error.
     * This call compares the argument error, static_cast’d to E, with the return value from error().
     * 
     * @tparam G the type of defaultError
     * @param[in] error the error to use if *this does not contain an error
     * @return true if *this contains an error that is equivalent to the given error
     * @return false otherwise
     * @uptrace{SWS_CORE_00765}
     */
    template <typename G>
    bool checkError (G &&error) const {
        return hasValue() ? false : (error() == static_cast<E>(std::forward<G>(error)));
    }

#ifndef ARA_NO_EXCEPTIONS
    /**
     * @brief Return the contained value or throw an exception. 
     * This function does not participate in overload resolution when the compiler toolchain does not support C++ 
     * exceptions.
     * 
     * @return const T& a const reference to the contained value
     * @uptrace{SWS_CORE_00766}
     */
    const T& valueOrThrow () const &noexcept(false) {
        if (hasValue()) {
            return std::get<T>(data_);
        }

        const E& e = std::get<E>(data_);
        e.ThrowAsException();
    }

    /**
     * @brief Return the contained value or throw an exception. 
     * This function does not participate in overload resolution when the compiler toolchain does not support C++ 
     * exceptions.
     * 
     * @return T&& an rvalue reference to the contained value
     * @uptrace{SWS_CORE_00769}
     */
    T&& valueOrThrow () &&noexcept(false) {
        if (hasValue()) {
            return std::move(std::get<T>(data_));
        }

        const E& e = std::get<E>(data_);
        e.ThrowAsException();
    }
#endif
private:
    template <typename F>
    using CallableReturnsXResult = 
                    std::enable_if_t<internal::is_result<std::result_of_t<F(T const&)>>::value>;

    template <typename F>
    using CallableReturnsNoXResult = 
                    std::enable_if_t<!internal::is_result<std::result_of_t<F(T const&)>>::value>;
public:
    /**
     * @brief Return the contained value or return the result of a function call.
     * If *this contains a value, it is returned. Otherwise, the specified callable is invoked and its return value 
     * which is to be compatible to type T is returned from this function. The Callable is expected to be compatible to 
     * this interface: T f(const E&);
     * 
     * @tparam F the type of the Callable f
     * @param[in] f the Callable
     * @return T the value
     * @uptrace{SWS_CORE_00767}
     */
    template <typename F>
    T resolve (F &&f) const {
        return hasValue() ? value() : std::forward<F>(f)(error());
    }

    /**
     * @brief Apply the given Callable to the value of this instance, and return a new XResult with the result of the 
     * call.
     * The Callable is expected to be compatible to one of these two interfaces: XResult<XXX, E> f(const T&); XXX f
     * (const T&); meaning that the Callable either returns a XResult<XXX> or a XXX directly, where XXX can be any type 
     * that is suitable for use by class XResult. 
     * The return type of this function is decltype(f(value())) for a template argument F that returns a XResult type, 
     * and it is XResult<decltype(f(value())), E> for a template argument F that does not return a XResult type.
     * If this instance does not contain a value, a new XResult<XXX, E> is still created and returned, with the original 
     * error contents of this instance being copied into the new instance
     * 
     * @tparam F the type of the Callable f
     * @param[in] f the Callable
     * @uptrace{SWS_CORE_00768}
     */
    template <typename F, typename = CallableReturnsXResult<F>>
    auto bind(F&& f) const -> decltype(f(value()))
    {
        using U = decltype(f(value()));
        return hasValue() ? std::forward<F>(f)(value()) : U(error());
    }

    template <typename F, typename = CallableReturnsNoXResult<F>>
    auto bind(F&& f) const -> XResult<decltype(f(value())), E>
    {
        using U = decltype(f(value()));
        using R = XResult<U, E>;
        return hasValue() ? std::forward<F>(f)(value()) : R(error());
    }

    template <typename, typename>
    friend class XResult;
private:
    std::variant<T, E> data_;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief Specialization of class XResult for "void" values.
 * 
 * @tparam E the type of error
 * @uptrace{SWS_CORE_00801}
 */
template <typename E>
class XResult<void, E> final {
     // Cannot use 'void' for std::variant, so define our own (default-constructible) dummy type instead.
    struct dummy{};
    using T = dummy;
    std::variant<T, E> data_;
public:
    using value_type = void; //@uptrace{SWS_CORE_00811}
    using error_type = E; //@uptrace{SWS_CORE_00812}

public:
    /**
     * @brief Construct a new XResult with a "void" value
     * 
     * @uptrace{SWS_CORE_00821}
     */
    XResult () noexcept :data_(T{}) {}

    /**
     * @brief Construct a new XResult from the specified error (given as lvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @uptrace{SWS_CORE_00823}
     */
    explicit XResult (const E &e) : data_(e) {}

    /**
     * @brief Construct a new XResult from the specified error (given as rvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @uptrace{SWS_CORE_00824}
     */
    explicit XResult (E &&e): data_(std::move(e)) {}

    /**
     * @brief Copy-construct a new XResult from another instance.
     * 
     * @param[in] other the other instance
     * @uptrace{SWS_CORE_00825}
     */
    XResult (const XResult &other) = default;

    /**
     * @brief Move-construct a new XResult from another instance
     * 
     * @param[in] other the other instance
     * @uptrace{SWS_CORE_00826}
     */
    XResult (XResult &&other) noexcept(std::is_nothrow_move_constructible<E>::value) = default;

    /**
     * @brief Destructor.
     * This destructor is trivial if std::is_trivially_destructible<E>::value is true.
     * 
     * @uptrace{SWS_CORE_00827}
     */
    ~XResult () noexcept { }

public:
    /**
     * @brief Build a new XResult with "void" as value
     * 
     * @return XResult a XResult that contains a "void" value
     * @uptrace{SWS_CORE_00831}
     */
    static XResult FromValue () noexcept { return XResult(); }

    /**
     * @brief Build a new XResult from the specified error (given as lvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @return XResult a XResult that contains the error e
     * @uptrace{SWS_CORE_00834}
     */
    static XResult FromError (const E &e) { return XResult(e); }

    /**
     * @brief Build a new XResult from the specified error (given as rvalue).
     * 
     * @param[in] e the error to put into the XResult
     * @return XResult a XResult that contains the error e
     * @uptrace{SWS_CORE_00835}
     */
    static XResult FromError (E &&e) { return XResult(std::move(e)); }

    /**
     * @brief Build a new XResult from an error that is constructed in-place from the given arguments.
     * This function shall not participate in overload resolution unless: std::is_constructible<E,Args&&...>::value is 
     * true, and the first type of the expanded parameter pack is not E, and the first type of the expanded parameter 
     * pack is not a specialization of XResult
     * 
     * @tparam Args... the types of arguments given to this function 
     * @param[in] args the parameter pack used for constructing the error
     * @return XResult a XResult that contains an error
     * @uptrace{SWS_CORE_00836}
     */
    template <typename... Args>
    static XResult FromError (Args &&... args) { return XResult(E{std::forward<Args>(args)...}); }

public:
    /**
     * @brief Copy-assign another XResult to this instance.
     * 
     * @param[in] other the other instance
     * @return XResult& *this, containing the contents of other
     * @uptrace{SWS_CORE_00841}
     */
    XResult& operator= (const XResult &other) = default;

    /**
     * @brief Move-assign another XResult to this instance
     * 
     * @param[in] other the other instance
     * @return XResult& *this, containing the contents of other
     * @uptrace{SWS_CORE_00842}
     */
    XResult& operator= (XResult &&other) noexcept(std::is_nothrow_move_constructible<E>::value && 
                                                std::is_nothrow_move_assignable<E>::value) = default;

    /**
     * @brief Put a new value into this instance, constructed in-place from the given arguments
     * 
     * @tparam Args... the types of arguments given to this function 
     * @param[in] args the arguments used for constructing the value
     * @uptrace{SWS_CORE_00843}
     */
    template <typename... Args>
    void emplaceValue (Args &&... args) noexcept { data_ = T(args...); }    

    /**
     * @brief Put a new error into this instance, constructed in-place from the given arguments.
     * 
     * @tparam Args.. the types of arguments given to this function 
     * @param[in] args the arguments used for constructing the error
     * @uptrace{SWS_CORE_00844}
     */
    template <typename... Args>
    void emplaceError (Args &&... args) {
        // Unfortunately, ara::core::std::variant does not support emplace(), so we fall back to assignment here.
        data_ = std::move(E(std::forward<Args>(args)...));
    }

    /**
     * @brief Exchange the contents of this instance with those of other
     * 
     * @param[in,out] other the other instance
     * @uptrace{SWS_CORE_00845}
     */
    void swap (XResult &other) noexcept(std::is_nothrow_move_constructible<E>::value && 
                                       std::is_nothrow_move_assignable<E>::value) {
        std::swap(data_, other.data_);
    }

public:
    /**
     * @brief Check whether *this contains a value.
     * 
     * @return true if *this contains a value
     * @return false otherwise
     * @uptrace{SWS_CORE_00851}
     */
    bool hasValue () const noexcept { return std::holds_alternative<T>(data_); }

    /**
     * @brief Check whether *this contains a value
     * 
     * @return true if *this contains a value
     * @return false otherwise
     * @uptrace{SWS_CORE_00852}
     */
    explicit operator bool () const noexcept { return hasValue(); }

    /**
     * @brief Do nothing.
     * This function only exists for helping with generic programming. 
     * The behavior of this function is undefined if *this does not contain a value
     * 
     * @uptrace{SWS_CORE_00853}
     */
    void operator* () const { /** do nothing */ }

    /**
     * @brief Do nothing.
     * This function only exists for helping with generic programming. 
     * The behavior of this function is undefined if *this does not contain a value
     * 
     * @uptrace{SWS_CORE_00855}
     */
    void Value () const { /** do nothing */ }

    /**
     * @brief Access the contained error. 
     * The behavior of this function is undefined if *this does not contain an error
     * 
     * @return const E& a const reference to the contained error
     * @uptrace{SWS_CORE_00857}
     */
    const E& error () const & { return std::get<E>(data_); }

    /**
     * @brief Access the contained error. 
     * The behavior of this function is undefined if *this does not contain an error
     * 
     * @return E&& an rvalue reference to the contained error
     * @uptrace{SWS_CORE_00858}
     */
    E&& error () && { return std::move(std::get<E>(data_)); }

    /**
     * @brief Return the contained error as an std::optional.
     * 
     * @return std::optional<E> an std::optional with the error, if present
     * @uptrace{SWS_CORE_00868} 
     */
    std::optional<E> err () const & { return hasValue() ? std::nullopt : std::optional<E>(error()); }

    /**
     * @brief Return the contained error as an std::optional.
     * 
     * @return std::optional<E> an std::optional with the error, if present
     * @uptrace{SWS_CORE_00869} 
     */
    std::optional<E> err () && { return hasValue() ? std::nullopt : std::optional<E>(std::move(error())); }

public:
    /**
     * @brief Do nothing.
     * This function only exists for helping with generic programming
     * 
     * @tparam U the type of defaultValue
     * @param[in] defaultValue the value to use if *this does not contain a value
     * @uptrace{SWS_CORE_00861} 
     */
    template <typename U>
    void valueOr (U &&defaultValue) const  { /** do nothing */ }

    /**
     * @brief Return the contained error or the given default error. 
     * If *this contains an error, it is returned. Otherwise, the specified default error is returned, 
     * static_cast’d to E.
     * 
     * @tparam G the type of defaultError
     * @param[in] defaultError the error to use if *this does not contain an error
     * @return E the error
     * @uptrace{SWS_CORE_00863} 
     */
    template <typename G>
    E errorOr (G &&defaultError) const & { 
        return hasValue() ? static_cast<E>(std::forward<G>(defaultError)) : error();
    }

    /**
     * @brief Return the contained error or the given default error.
     * If *this contains an error, it is std::move’d into the return value. Otherwise, the specified default error is 
     * returned, static_cast’d to E.
     * 
     * @tparam G the type of defaultError
     * @param[in] defaultError the error to use if *this does not contain an error
     * @return E the error
     * @uptrace{SWS_CORE_00864} 
     */
    template <typename G>
    E errorOr (G &&defaultError) && {
        return hasValue() ? static_cast<E>(std::forward<G>(defaultError)) : std::move(error());
    }

    /**
     * @brief Return whether this instance contains the given error. 
     * This call compares the argument error, static_cast’d to E, with the return value from error().
     * 
     * @tparam G the type of the error argument error
     * @param[in] error the error to check
     * @return true if *this contains an error that is equivalent to the given error
     * @return false otherwise
     * @uptrace{SWS_CORE_00865} 
     */
    template <typename G>
    bool checkError (G &&error) const {
        return hasValue() ? false : (error() == static_cast<E>(std::forward<G>(error)));
    }

#ifndef ARA_NO_EXCEPTIONS
    /**
     * @brief Return the contained value or throw an exception. 
     * This function does not participate in overload resolution when the compiler toolchain does not support C++ 
     * exceptions.
     * 
     * @uptrace{SWS_CORE_00866} 
     */
    void valueOrThrow () const noexcept(false) {
        if (!hasValue()) {
            E const& e = error();
            e.ThrowAsException();
        }
    }
#endif
private:
    template <typename F>
    using CallableReturnsXResult = std::enable_if_t<internal::is_result<std::result_of_t<F()>>::value>;

    template <typename F>
    using CallableReturnsNoXResult = std::enable_if_t<!internal::is_result<std::result_of_t<F()>>::value>;
public:
    /**
     * @brief Do nothing or call a function. 
     * If *this contains a value, this function does nothing. Otherwise, the specified callable is invoked. The 
     * Callable is expected to be compatible to this interface: void f(const E&); 
     * This function only exists for helping with generic programming
     * 
     * @tparam F the type of the Callable f
     * @param[in] f the Callable
     * @uptrace{SWS_CORE_00867} 
     */
    template <typename F>
    void resolve (F &&f) const {
        if (!hasValue()) {
            std::forward<F>(f)(error());
        } 
    }

    /**
     * @brief Call the given Callable, and return a new XResult with the result of the call.
     * The Callable is expected to be compatible to one of these two interfaces: XResult<XXX, E> f(); XXX f(); meaning 
     * that the Callable either returns a XResult<XXX, E> or a XXX directly, where XXX can be any type that is suitable 
     * for use by class XResult. 
     * The return type of this function is decltype(f()) for a template argument F that returns a XResult type, and it 
     * is XResult<decltype(f()), E> for a template argument F that does not return a XResult type.
     * If this instance does not contain a value, a new XResult<XXX, E> is still created and returned, with the original 
     * error contents of this instance being copied into the new instance.
     * 
     * @tparam F the type of the Callable f
     * @param[in] f the Callable
     * @uptrace{SWS_CORE_00870} 
     */
    template <typename F, typename = CallableReturnsXResult<F>>
    auto bind(F&& f) const -> decltype(f()) {
        using U = decltype(f());
        return hasValue() ? std::forward<F>(f)() : U(error());
    }

    template <typename F, typename = CallableReturnsNoXResult<F>>
    auto bind(F&& f) const -> XResult<decltype(f()), E> {
        using U = decltype(f());
        using R = XResult<U, E>;
        return hasValue() ? std::forward<F>(f)() : R(error());
    }

    template <typename, typename>
    friend class XResult;
};


///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief Compare two XResult instances for equality.
 * A XResult that contains a value is unequal to every XResult containing an error. A XResult is equal to another XResult 
 * only if both contain the same type, and the value of that type compares equal
 * 
 * @param[in] lhs the left hand side of the comparison
 * @param[in] rhs the right hand side of the comparison
 * @return true if the two instances compare equal
 * @return false otherwise
 * @uptrace{SWS_CORE_00780} 
 */
template <typename T, typename E>
bool operator== (const XResult<T, E> &lhs, const XResult<T, E> &rhs) {
    if (bool(lhs) != bool(rhs)){
        return false;
    }
        
    if (lhs){
        return lhs.value() == rhs.value();
    }
        
    return lhs.error() == rhs.error();
}

/**
 * @brief Compare two XResult instances for equality.
 * A XResult that contains a value is unequal to every XResult containing an error. A XResult is equal to another XResult 
 * only if both contain the same type, and the value of that type compares equal
 * 
 * @param[in] lhs the left hand side of the comparison
 * @param[in] rhs the right hand side of the comparison
 * @return true if the two instances compare unequal
 * @return false otherwise
 * @uptrace{SWS_CORE_00781} 
 */
template <typename T, typename E>
bool operator!= (const XResult<T, E> &lhs, const XResult<T, E> &rhs) {
    if (bool(lhs) != bool(rhs)){
        return true;
    }
        
    if (lhs){
        return lhs.value() != rhs.value();
    }
        
    return lhs.error() != rhs.error();    
}


/**
 * @brief Compare a XResult instance for equality to a value. 
 * A XResult that contains no value is unequal to every value. A XResult is equal to a value only if the XResult contains 
 * a value of the same type, and the values compare equal.
 * 
 * @param[in] lhs the XResult instance
 * @param[in] rhs the value to compare with
 * @return true if the XResult’s value compares equal to the rhs value
 * @return false otherwise
 * @uptrace{SWS_CORE_00782} 
 */
template <typename T, typename E>
bool operator== (const XResult<T, E> &lhs, const T &rhs) { return lhs ? (*lhs == rhs) : false; }

/**
 * @brief Compare a XResult instance for equality to a value. 
 * A XResult that contains no value is unequal to every value. A XResult is equal to a value only if the XResult contains 
 * a value of the same type, and the values compare equal.
 * 
 * @param[in] lhs the value to compare with
 * @param[in] rhs the XResult instance
 * @return true if the XResult’s value compares equal to the lhs value
 * @return false otherwise
 * @uptrace{SWS_CORE_00783} 
 */
template <typename T, typename E>
bool operator== (const T &lhs, const XResult<T, E> &rhs) { return rhs ? (lhs == *rhs) : false; }

/**
 * @brief Compare a XResult instance for equality to a value. 
 * A XResult that contains no value is unequal to every value. A XResult is equal to a value only if the XResult contains 
 * a value of the same type, and the values compare equal.
 * 
 * @param[in] lhs the XResult instance
 * @param[in] rhs the value to compare with
 * @return true if the XResult’s value compares unequal to the rhs value
 * @return false otherwise
 * @uptrace{SWS_CORE_00784} 
 */
template <typename T, typename E>
bool operator!= (const XResult<T, E> &lhs, const T &rhs) { return lhs ? (*lhs != rhs) : true; }

/**
 * @brief Compare a XResult instance for equality to a value. 
 * A XResult that contains no value is unequal to every value. A XResult is equal to a value only if the XResult contains 
 * a value of the same type, and the values compare equal.
 * 
 * @param[in] lhs the value to compare with
 * @param[in] rhs the XResult instance
 * @return true if the XResult’s value compares unequal to the lhs value
 * @return false otherwise
 * @uptrace{SWS_CORE_00785} 
 */
template <typename T, typename E>
bool operator!= (const T &lhs, const XResult<T, E> &rhs) { return rhs ? (lhs != *rhs) : true; }

/**
 * @brief Compare a XResult instance for equality to an error. 
 * A XResult that contains no error is unequal to every error. A XResult is equal to an error only if the XResult contains 
 * an error of the same type, and the errors compare equal.
 * 
 * @param[in] lhs the XResult instance
 * @param[in] rhs the error to compare with
 * @return true if the XResult’s error compares equal to the rhs error
 * @return false otherwise
 * @uptrace{SWS_CORE_00786} 
 */
template <typename T, typename E>
bool operator== (const XResult<T, E> &lhs, const E &rhs) { return lhs ? false : (lhs.error() == rhs); }

/**
 * @brief Compare a XResult instance for equality to an error. 
 * A XResult that contains no error is unequal to every error. A XResult is equal to an error only if the XResult contains 
 * an error of the same type, and the errors compare equal.
 * 
 * @param[in] lhs the error to compare with
 * @param[in] rhs the XResult instance
 * @return true if the XResult’s error compares equal to the lhs error
 * @return false otherwise
 * @uptrace{SWS_CORE_00787} 
 */
template <typename T, typename E>
bool operator== (const E &lhs, const XResult<T, E> &rhs) {  return rhs ? false : (lhs == rhs.error()); }

/**
 * @brief Compare a XResult instance for equality to an error. 
 * A XResult that contains no error is unequal to every error. A XResult is equal to an error only if the XResult contains 
 * an error of the same type, and the errors compare equal.
 * 
 * @param[in] lhs the XResult instance
 * @param[in] rhs the error to compare with
 * @return true if the XResult’s error compares unequal to the rhs error
 * @return false otherwise
 * @uptrace{SWS_CORE_00788} 
 */
template <typename T, typename E>
bool operator!= (const XResult<T, E> &lhs, const E &rhs) { return lhs ? true : (lhs.error() != rhs); }

/**
 * @brief Compare a XResult instance for equality to an error. 
 * A XResult that contains no error is unequal to every error. A XResult is equal to an error only if the XResult contains 
 * an error of the same type, and the errors compare equal.
 * 
 * @param[in] lhs the error to compare with
 * @param[in] rhs the XResult instance
 * @return true if the XResult’s error compares unequal to the lhs error
 * @return false otherwise
 * @uptrace{SWS_CORE_00789} 
 */
template <typename T, typename E>
bool operator!= (const E &lhs, const XResult<T, E> &rhs) { return rhs ? true : (lhs != rhs.error()); }

/**
 * @brief swap the contents of the two given arguments
 * 
 * @param[in,out] lhs one instance
 * @param[in,out] rhs another instance
 * @uptrace{SWS_CORE_00796} 
 */
template <typename T, typename E>
inline void Swap (XResult<T, E> &lhs, XResult<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
    lhs.swap(rhs);
}

using XResultCode = XResult<void>;

} //namespace xbus::core


#endif /** !__XBUS_CORE_XRESULT_H__ */